#ifndef __aNScriptLoader__h__
#define __aNScriptLoader__h__

#include "neiderra/core/Base.h"
#include "neiderra/core/io/IOStream.h"
#include "neiderra/core/iException.h"
#include "neiderra/utils/Lexer.h"
#include "nsNodes.h"

#include <vector>
#include <map>
#include <string>
#include <stack>
#include <memory>

using namespace neiderra::core;
using namespace neiderra::utils;
using namespace neiderra::utils::lexer;
using namespace std;

namespace neiderra {
	namespace utils {
		namespace script {

			class ScopeManager;
			class nsAsmGenerator;
			class Symtable;

			struct nsNode;
			struct nsVar;
			struct nsFunc;
			struct nsClass;
			struct nsExpr;
			struct nsRef;
			struct nsFactor;
			struct nsTerm;
			struct nsLvalue;
			struct nsOperand;
			struct nsFCall;
			struct nsArg;
			struct nsStmt;
			struct nsBlock;
			struct nsMethod;
			struct nsField;
			
			// Loader for script
			class NScriptLoader: public Base {
			public:
				NScriptLoader(Core*);
				virtual ~NScriptLoader();

				virtual nsNode* loadFromStream(iInputStream*);
				virtual Symtable* getSymtable();
				
				//
				// Exceptions
				//

				// fixme: EParseException, line

				class EBadVar : public iException {
					public:	
						EBadVar(const string& s): iException(s) {}
						virtual string getReport() const { return "Var parse error: " + _message; }
				};
				
				class EBadClass : public iException {
					public:	
						EBadClass(const string& s): iException(s) {}
						virtual string getReport() const { return "Class parse error: " + _message; }
				};
				
				class EBadFunc: public iException {
					public:	
						EBadFunc(const string& s): iException(s) {}
						virtual string getReport() const { return "Function or method parse error: " + _message; }
				};
				
				class EBadToken: public iException {
					public:	
						EBadToken(const string& s): iException(s) {}
						virtual string getReport() const { return "Generic parse error: " + _message; }
				};
				
			private:
				void prepareSymtable();

				// match expected and real tokens
				void match(tokens);
				// returns true if expected and real tokens are equal
				bool next(tokens);
				// returns true if next token is a type
				bool nextIsType();
				// go to next token
				void pass();

				nsNode* searchForDec(const string& name);
				nsClass* getTypeNode(const string& type_name);
				string getNearestNameOf(const string& name);

				// returns true if the string is the embedded type
				bool isEmbeddedType(const string& s);
				// returns true if the string is a type
				bool isType(const string& s);

				unsigned typesize(nsNode*);

				nsFunc* read_func( nsNode* parent);
				nsFunc* read_def_func( nsFunc* parent );
				nsClass* read_class ( nsNode* parent );
				nsOperand* read_expr( nsNode* parent ); // <-
				nsOperand* read_term( nsNode* parent );
				nsOperand* read_factor( nsNode* parent );
				nsNode* read_stmt( nsBlock* parent );	// ->
				nsVar* read_dec_var( nsNode* parent );
				nsNode* read_block( nsNode* parent );
				nsNodes* read_formal_args( nsFunc* parent );
				nsNodes* read_actual_args();
				nsOperand* read_deref( nsOperand* );
				
				inline Token getCurrentToken() { return _currentToken; }
				Token _currentToken;

				iInputStream* input_;
				std::auto_ptr<Lexer> lexer_;
				
				Symtable* _symtable;
				ScopeManager* scoper_;

				// embedded types
				nsClass* tInt, *tChar, *tFloat, *tBool, *tDouble, *tVoid;

				// useful pointers
				nsClass* _currentClass;
				nsFunc* _currentFunc;

				inline nsFunc* getCurrentFunction() { return _currentFunc; }
				inline nsClass* getCurrentClass() { return _currentClass; }
				inline void setCurrentFunction(nsFunc* f) { _currentFunc = f; }
				inline void setCurrentClass(nsClass* c) { _currentClass = c; }
			};

		}
	}
}

#endif
